//Special thanks to Jonathan Willis for showing me the err of my concurrent ways

//Treat each line of stdin as the arguments of a command.
//
//Whereas xargs(1) does:
//      line0   \
//      line1   |
//      etc...  >- |-> cmd line0 line1 etc... lineN-1 lineN
//      lineN-1 |
//      lineN   /
//
// each(1) does:
//      line0   \     / cmd line0
//      line1   |     | cmd line1
//      etc...  > |-> < etc...
//      lineN-1 |     | cmd lineN-1
//      lineN   /     \ cmd lineN
//
// If max_procs > 1 the results will be displayed in order of completion
// and not order of arrival unless the -o flag is specified.
package main

import (
	"os"
	"flag"
	"log"
	"runtime"
	"bufio"
	"strings"
	"exec"
	"container/heap"
	"container/vector"
)

var max_procs = flag.Int("P", 0, `Run up to max_procs at a time. If 0,
defaults to $GOMAXPROCS.`)
var ordered = flag.Bool("o", false, "Report outputs in order.")

type output struct {
	pid int
	buf []byte
}

//used by oprinter
func (o *output) Less(y interface{}) bool {
	return o.pid < y.(*output).pid
}

var (
	quit      = make(chan bool)
	input     = make(chan string)
	out       = make(chan *output)
	prog, pwd string
)

var splitter func(string) []string

func splitfunc(ifs string) func(string) []string {
	if len(ifs) == 0 {
		return func(in string) []string { return []string{in} }
	}

	runes := []int(ifs)
	if len(runes) == 1 {
		return func(in string) []string {
			return strings.Split(in, ifs, -1)
		}
	}

	is_ifs_rune := func(r int) bool {
		for _, rune := range runes {
			if r == rune {
				return true
			}
		}
		return false
	}
	return func(in string) []string {
		return strings.FieldsFunc(in, is_ifs_rune)
	}
}

func prnln(b []byte) {
	os.Stdout.Write(b)
	if b[len(b)-1] != '\n' {
		os.Stdout.WriteString("\n")
	}
}

func uprinter() {
	for in := range out {
		prnln(in.buf)
	}
	quit <- true
}

func oprinter() {
	head := 1
	p_ := vector.Vector(make([]interface{}, 0, *max_procs*2))
	p := &p_ //no need to Init, since we start with zero elms
	tap := func() {
		for p.Len() > 0 && p.At(0).(*output).pid == head {
			top := heap.Pop(p).(*output)
			prnln(top.buf)
			head++
		}
	}
	for in := range out {
		heap.Push(p, in)
		tap()
	}
	quit <- true
}

func run(line string, pid int) *output {
	//TODO: curry with argv[2:]?
	out, err := exec.Command(prog, splitter(line)...).Output()
	if err != nil {
		log.Fatalln(err, "\non", line)
	}
	return &output{pid, out}
}

func limiter(n int) {
	active, pid := 0, 0
	done := make(chan bool)
	for line := range input {
		if active == n {
			<-done
			active--
		}
		active++
		pid++
		go func(ln string, p int) {
			defer func() { done <- true }()
			out <- run(ln, p)
		}(line, pid)
	}
	for ; active > 0; active-- {
		<-done
	}
	close(out)
}

//Usage: %name %flags command
func main() {
	log.SetFlags(0)
	flag.Parse()
	if flag.NArg() != 1 {
		log.Fatalln("Usage: each [-P] [-o] cmd")
	}

	if *max_procs == 0 {
		*max_procs = runtime.GOMAXPROCS(0)
	}

	//set globals
	splitter = splitfunc(os.Getenv("IFS"))
	var err os.Error
	prog, err = exec.LookPath(flag.Args()[0])
	if err != nil {
		log.Fatalln(flag.Args()[0], "not found:", err)
	}
	pwd, err = os.Getwd()
	if err != nil {
		log.Fatalln(err)
	}

	//launch servers
	go limiter(*max_procs)

	if *ordered {
		go oprinter()
	} else {
		go uprinter()
	}

	//read input
	stdin := bufio.NewReader(os.Stdin)
	for {
		line, err := stdin.ReadString('\n')
		if err != nil && len(line) == 0 {
			break
		}
		input <- line[:len(line)-1]
	}
	close(input)
	<-quit
}
